/*
 * Copyright (c) 2007-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.livejournal;

import java.awt.Component;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.text.SimpleDateFormat;
import java.util.EventObject;
import java.util.TreeMap;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import org.panteleyev.blogclient.BloggerFrame;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.Options;
import org.panteleyev.blogclient.PetrusBloggerFileFilter;
import org.panteleyev.blogclient.events.PostDeletedEvent;
import org.panteleyev.livejournal.events.CommentsUpdatedEvent;
import org.panteleyev.ljapi.LJAccount;
import org.panteleyev.ljapi.LJComment;
import org.panteleyev.ljapi.LJCommentMetaData;
import org.panteleyev.ljapi.LJEvent;
import org.panteleyev.ljapi.LJException;
import org.panteleyev.utilities.events.AppEventListener;

public class LJCommentsFrame extends BloggerFrame<LJUserProfile> {

    private class CommentsTreeCellRenderer extends DefaultTreeCellRenderer {
        private LJAccount _account;

        CommentsTreeCellRenderer(LJAccount account) {
            _account = account;
        }

        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value,
                                           boolean selected,
                                           boolean expanded,
                                           boolean leaf,
                                           int row,
                                           boolean hasFocus) {

            super.getTreeCellRendererComponent(tree, value,
                                           selected,
                                           expanded,
                                           leaf,
                                           row,
                                           hasFocus);

            StringBuilder text = new StringBuilder();

            DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
            Object userObject = node.getUserObject();
            if (userObject instanceof LJComment) {
                LJComment comm = (LJComment)userObject;

                setIcon(Icon.FRIEND_HEAD.i());
                setIconTextGap(2);

                String user = _account.getUserName(comm.getPosterID());

                text.append("<html><b>").append(user).append("</b> ");

                if (comm.getDate() != null) {
                    text.append(LJCommentsFrame.dateFormat.format(comm.getDate())).append(" ");
                }

                if (comm.isScreened()) {
                    text.append(L10N.COMMENTS_FRAME_COMMENT_SCREENED.s()).append(" ");
                }

                if (comm.isDeleted()) {
                    text.append(L10N.COMMENTS_FRAME_COMMENT_DELETED.s());
                } else {
                    if (comm.getSubject() == null) {
                        text.append(L10N.COMMENTS_FRAME_COMMENT_NO_SUBJECT.s());
                    } else {
                        text.append(comm.getSubject());
                    }
                }

                text.append("</html>");
            }

            setText(text.toString());
            return this;
        }
    }

    private LJEvent                 _event;
    private DefaultMutableTreeNode  _treeRoot;
    private DefaultTreeModel        _treeModel;
    private LJAccount               _account;

    static SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy kk:mm");

    public LJCommentsFrame(LJUserProfile profile, LJEvent event) throws LJException {
        super(profile);

        _account = getProfile().getAccount();
        _event = event;


        initComponents();

        _commentsTreeView.setCellRenderer(new CommentsTreeCellRenderer(_account));

        loadComments();
        createMenuBar();

        StringBuilder b = new StringBuilder(L10N.COMMENTS_FRAME_TITLE.s());
        b.append(" [");
        b.append(event.getId());
        b.append("] ");
        b.append(event.getBody());
        setTitle(b.toString());

        addAppEventListener(PostDeletedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                onPostDeletedEvent((PostDeletedEvent)event);
            }
        });

        addAppEventListener(CommentsUpdatedEvent.class, new AppEventListener() {
            @Override
            public void eventReceived(EventObject event) {
                onCommentsUpdatedEvent((CommentsUpdatedEvent)event);
            }
        });

        Options.getBounds(this);
    }

    private void createMenuBar() {
        createMenuBar(
            createFileMenu(),
            createProfileMenu(),
            createToolsMenu(),
            createWindowMenu(),
            createHelpMenu()
        );
    }

    public String getItemID() { return _event.getId(); }

    private void loadComments() throws LJException {
        _treeRoot = new DefaultMutableTreeNode();

        /* Get everything from disk */
        File commDir = getProfile().getCommentsPostingDirectory(getItemID());

        TreeMap<Integer,DefaultMutableTreeNode> commHash = new TreeMap<>();
        ObjectInputStream in = null;
        try {
            File[] cFiles = commDir.listFiles(new PetrusBloggerFileFilter());
            for (File f : cFiles) {
                in = new ObjectInputStream(new FileInputStream(f));
                LJComment comm = (LJComment)in.readObject();

                /* Update state from metadata */
                LJCommentMetaData meta = _account.getCommentMeta(comm.getID());
                comm.updateFromMeta(meta);

                commHash.put(comm.getID(), new DefaultMutableTreeNode(comm));
            }

        } catch (IOException | ClassNotFoundException e) {
            throw new LJException();
        }

        /*
         * Build tree
         */
        for (Integer key : commHash.keySet()) {
            DefaultMutableTreeNode tNode = commHash.get(key);
            int parentID = ((LJComment)tNode.getUserObject()).getParentID();

            if (parentID == 0) {
                _treeRoot.add(tNode);
            } else {
                DefaultMutableTreeNode pNode = commHash.get(parentID);
                if (pNode == null) {
                    _treeRoot.add(tNode);
                } else {
                    pNode.add(tNode);
                }
            }
        }

        /* Set the model */
        _treeModel = new DefaultTreeModel(_treeRoot);
        _commentsTreeView.setModel(_treeModel);
    }

    private void showComment(LJComment comm) {
        StringBuilder b = new StringBuilder("<html><body bgcolor='white'>")
            .append("<table border='0' width='100%'>")
            .append("<tr bgcolor='#C0C0C0' valign='top'><td>");

        String user = _account.getUserName(comm.getPosterID());
        b.append("<b>").append(user).append("</b>");

        if (comm.getDate() != null) {
            b.append("<br>").append(dateFormat.format(comm.getDate()));
        }

        if (comm.getIP() != null) {
            b.append("<br>(").append(comm.getIP()).append(")");
        }


        b.append("<td>");
        String subj = comm.getSubject();
        if (subj == null) {
            b.append(L10N.COMMENTS_FRAME_COMMENT_NO_SUBJECT.s());
        } else {
            b.append(subj);
        }


        b.append("<tr><td colspan='2'>");

        if (comm.getBody() != null) {
            b.append(comm.getBody());
        }

        b.append("</table></body></html>");
        _commentView.setText(b.toString());
    }

    private void onPostDeletedEvent(PostDeletedEvent event) {
        if (event.getPost().equals(_event)) {
            closeFrame(true);
        }
    }

    private void onCommentsUpdatedEvent(CommentsUpdatedEvent event) {
        try {
            loadComments();
        } catch (LJException e) {
            handleException(e);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jSplitPane1 = new javax.swing.JSplitPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        _commentsTreeView = new javax.swing.JTree();
        jScrollPane2 = new javax.swing.JScrollPane();
        _commentView = new javax.swing.JEditorPane();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);

        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        _commentsTreeView.setRootVisible(false);
        _commentsTreeView.setShowsRootHandles(true);
        _commentsTreeView.setVisibleRowCount(15);
        _commentsTreeView.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                _commentsTreeViewValueChanged(evt);
            }
        });
        jScrollPane1.setViewportView(_commentsTreeView);

        jSplitPane1.setTopComponent(jScrollPane1);

        _commentView.setContentType("text/html");
        _commentView.setEditable(false);
        jScrollPane2.setViewportView(_commentView);

        jSplitPane1.setBottomComponent(jScrollPane2);

        getContentPane().add(jSplitPane1, java.awt.BorderLayout.CENTER);

        java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-852)/2, (screenSize.height-634)/2, 852, 634);
    }// </editor-fold>//GEN-END:initComponents

private void _commentsTreeViewValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event__commentsTreeViewValueChanged
        TreePath path = _commentsTreeView.getSelectionPath();
        if (path != null) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent();
            if (node != null) {
                Object userObject = node.getUserObject();
                if (userObject instanceof LJComment) {
                    showComment((LJComment)userObject);
                }
            }
        }
}//GEN-LAST:event__commentsTreeViewValueChanged


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JEditorPane _commentView;
    private javax.swing.JTree _commentsTreeView;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    // End of variables declaration//GEN-END:variables

}
